from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from goods.models import SKU
from meiduo_mall.utils import meiduo_json
from . import contants
from .serializers import CartSerializer, CartUpdateSerializer
from rest_framework import status
from django_redis import get_redis_connection


class CartView(APIView):
    def perform_authentication(self, request):
        # 取消身份验证
        pass

    def post(self, request):
        # 添加购物车数据
        # 接收
        sku_id = request.data.get('sku_id')
        count = request.data.get('count')

        # 验证
        if not all([sku_id, count]):
            raise serializers.ValidationError('参数不完整')
        # sku_id有效
        try:
            sku = SKU.objects.get(pk=sku_id)
        except:
            raise serializers.ValidationError('无效的商品编号')
        # 数量必须>=1
        try:
            count = int(count)
        except:
            raise serializers.ValidationError('无效的数量')
        if count < 1:
            raise serializers.ValidationError('无效的数量')
        # 判断库存是否足够
        if sku.stock < count:
            raise serializers.ValidationError('库存不足')

        # 处理：添加购物车
        response = Response({
            'sku_id': sku_id,
            'count': count,
            'selected': True
        })

        try:
            user = request.user  # 本质为方法，会读取配置，根据请求报文中的信息进行身份验证，如果验证失败则抛异常
        except:
            # 当前提供的身份信息无效，处于未登录存cookie
            # 先从cookie中读取现有购物车数据，在此基础上添加新的购物车数据，再写cookie
            cart_str = request.COOKIES.get('cart')
            if cart_str is None:
                cart_dict = {}
            else:
                cart_dict = meiduo_json.loads(cart_str)
            # 修改字典，增加或修改商品的数量
            cart_dict[sku_id] = {
                'count': count,  # +cart_dict[sku_id]['count'],
                'selected': True
            }
            # 修改cookie中购物车数据
            response.set_cookie('cart', meiduo_json.dumps(cart_dict), max_age=contants.CART_COOKIE_EXPIRES)
        else:
            # 已登录存redis
            redis_cli = get_redis_connection('cart')
            # 1.向hash中存商品编号、数量
            redis_cli.hset('cart%d' % user.id, sku_id, count)
            # 2.向set中存商品编号
            redis_cli.sadd('cart_selected%d' % user.id, sku_id)

        # 响应
        return response

    def get(self, request):
        # 查询购物车数据

        try:
            user = request.user
        except:
            # cookie版
            cart_str = request.COOKIES.get('cart')
            if cart_str is None:
                return Response('')
            # 将字符串转换为字典
            cart_dict = meiduo_json.loads(cart_str)
            # 获取字典中键的列表，表示商品编号列表
            sku_ids = cart_dict.keys()

            # 根据编号查询商品对象
            skus = SKU.objects.filter(pk__in=sku_ids)

            # 遍历商品对象，添加count、seleted属性
            for sku in skus:
                # 增加数量属性
                sku.count = cart_dict[sku.id]['count']
                # 增加选中状态属性
                sku.selected = cart_dict[sku.id]['selected']
        else:
            # redis版
            redis_cli = get_redis_connection('cart')
            # 从hash中读取商品的编号、数量{sku_id1:count1,sku_id2:count2,....}
            sku_dict = redis_cli.hgetall('cart%d' % user.id)
            # 从set中读取商品编号[sku_id1,sku_id2,...]
            sku_set = redis_cli.smembers('cart_selected%d' % user.id)

            # 从redis中读取的数据为bytes类型，转换
            sku_dict = {int(sku_id): int(count) for sku_id, count in sku_dict.items()}
            sku_set = [int(sku_id) for sku_id in sku_set]

            # 查询所有商品对象
            skus = SKU.objects.filter(pk__in=sku_dict.keys())

            # 遍历，增加数量、选中状态属性
            for sku in skus:
                sku.count = sku_dict.get(sku.id)
                sku.selected = sku.id in sku_set

        # 序列化输出
        serializer = CartSerializer(skus, many=True)
        return Response(serializer.data)

    def put(self, request):
        # 接收数据并验证
        serializer = CartUpdateSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        response = Response(serializer.validated_data)
        # 处理：修改
        sku_id = serializer.validated_data['sku_id']
        count = serializer.validated_data['count']
        selected = serializer.validated_data['selected']

        try:
            user = request.user
        except:
            # cookie版
            cart_str = request.COOKIES.get('cart')
            if cart_str is None:
                raise serializers.ValidationError('无购物车数据，无法修改')
            cart_dict = meiduo_json.loads(cart_str)
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }
            response.set_cookie('cart', meiduo_json.dumps(cart_dict), max_age=contants.CART_COOKIE_EXPIRES)
        else:
            # redis版
            redis_cli = get_redis_connection('cart')
            # 修改数量
            redis_cli.hset('cart%d' % user.id, sku_id, count)
            # 修改选中状态
            if selected:
                # 选中，则向set中添加商品编号
                redis_cli.sadd('cart_selected%d' % user.id, sku_id)
            else:
                # 不选中，则从set中删除商品编号
                redis_cli.srem('cart_selected%d' % user.id, sku_id)

        return response

    def delete(self, request):
        sku_id = request.data.get('sku_id')

        # 删除
        response = Response(status=status.HTTP_204_NO_CONTENT)

        try:
            user = request.user
        except:
            # cookie版
            cart_str = request.COOKIES.get('cart')
            if cart_str is None:
                raise serializers.ValidationError('暂无购物车数据，无法删除')
            cart_dict = meiduo_json.loads(cart_str)
            if sku_id in cart_dict:
                del cart_dict[sku_id]
            response.set_cookie('cart', meiduo_json.dumps(cart_dict), max_age=contants.CART_COOKIE_EXPIRES)
        else:
            redis_cli = get_redis_connection('cart')
            # 删除hash中商品
            redis_cli.hdel('cart%d' % user.id, sku_id)
            # 删除set中商品
            redis_cli.srem('cart_selected%d' % user.id, sku_id)

        return response


class CartSelectView(APIView):
    def perform_authentication(self, request):
        pass

    def put(self, request):
        # 接收
        selected = request.data.get('selected')

        # 修改所有商品的选中状态
        response = Response({'message': 'ok'})

        try:
            user = request.user
        except:
            # cookie版
            cart_str = request.COOKIES.get('cart')
            if cart_str is None:
                raise serializers.ValidationError('无购物车数据')
            cart_dict = meiduo_json.loads(cart_str)
            for key, value in cart_dict.items():
                value['selected'] = selected
            response.set_cookie('cart', meiduo_json.dumps(cart_dict), max_age=contants.CART_COOKIE_EXPIRES)
        else:
            # redis
            redis_cli = get_redis_connection('cart')

            # 增加或删除set中的数据
            if selected:
                # 从hash中获取当前用户购物车中所有商品编号
                sku_ids = redis_cli.hkeys('cart%d' % user.id)
                sku_ids = [int(sku_id) for sku_id in sku_ids]  # [1,2,3,4]

                redis_cli.sadd('cart_selected%d' % user.id, *sku_ids)
            else:
                redis_cli.delete('cart_selected%d' % user.id)

        return response
